﻿namespace GCC.Container.Abstracts
{
    /// <summary>
    /// GCC上下文
    /// </summary>
    public abstract class AbsGCCContext : IDisposable
    {
        /// <summary>
        /// 是否已经销毁
        /// </summary>
        private bool _disposed = false;

        /// <summary>
        /// 析构函数
        /// </summary>
        ~AbsGCCContext()
        {
            Dispose(false);
        }

        /// <summary>
        /// 销毁
        /// </summary>
        public virtual void Dispose(bool flag)
        {
            if (_disposed)
            {
                return;
            }
            _disposed = true;
        }

        /// <summary>
        /// 销毁
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }


        /// <summary>
        /// 唯一序号
        /// </summary>
        protected string _eventID = Guid.NewGuid().ToString("N");



        /// <summary>
        /// 事件ID,每次请求/事件都会发生变化
        /// </summary>
        public string EventID { get => _eventID; }

        /// <summary>
        /// 当前点名称
        /// </summary>
        protected string _pointName = "UnKnow";



        /// <summary>
        /// 当前节点名称
        /// </summary>
        public string? PointName => _pointName;


        /// <summary>
        /// 当websocket链接/断开事件
        /// 你会收到一个客户端实现的名称
        /// </summary>        
        public event Action<string>? OnWSConnectChanged;

        /// <summary>
        /// 根据路径获取配置
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public abstract string GetConfigByPath(string[] path);

        /// <summary>
        /// 根据key获取某一个缓存,自己用优先级判定
        /// </summary>
        /// <typeparam name="T">目标类型</typeparam>
        /// <param name="key">key</param>
        /// <param name="cancellationToken">取消token</param>
        /// <returns>从缓存获取的值,可能为空</returns>
        public abstract Task<T> TryGet<T>(string key, CancellationToken cancellationToken = default);

        /// <summary>
        /// 根据Key获取,如果获取不到则新增
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="key">key</param>
        /// <param name="onSet">当获取不到的时候从哪获取值</param>
        /// <param name="settings">过期时间</param>
        /// <param name="cancellationToken">取消token</param>
        /// <returns></returns>
        public abstract Task<T> GetOrSet<T>(string key, Func<Task<T>> onSet, CacheItemSettings settings, CancellationToken cancellationToken = default);

        /// <summary>
        /// 设置一个值到缓存
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="key">key</param>
        /// <param name="data">数据</param>
        /// <param name="settings">过期时间</param>
        /// <param name="cancellationToken">取消token</param>
        public abstract Task Set<T>(string key, T data, CacheItemSettings settings, CancellationToken cancellationToken = default);

        /// <summary>
        /// 移除一个缓存
        /// </summary>
        /// <param name="key">key</param>
        /// <param name="cancellationToken">取消token</param>
        public abstract Task Remove(string key, CancellationToken cancellationToken = default);

        /// <summary>
        /// 刷新一下某个缓存
        /// </summary>
        /// <param name="key">key</param>
        /// <param name="cancellationToken">取消token</param>
        public abstract Task Refresh(string key, CancellationToken cancellationToken = default);


        /// <summary>
        /// 根据Token获取用户
        /// </summary>
        /// <param name="token">token</param> 
        /// <returns>用户信息</returns>
        public abstract Task<GCCUserInfo?> GetUserByToken(string token);
        /// <summary>
        /// 获取用户的所有登陆信息
        /// </summary>
        /// <param name="uid">用户ID</param>
        /// <returns></returns>
        public abstract Task<List<GCCUserLoginInfos>> GetUserLoginInfos(long uid);


        /// <summary>
        /// 设置用户信息,如果不存在则新增
        /// </summary>
        /// <param name="request">登陆信息</param>
        /// <param name="accessToken">当前人员的访问Token</param>
        /// <returns></returns>
        public abstract Task<bool> SetUser(GCCUserLoginInfos request, string accessToken);


        /// <summary>
        /// 设置用户的角色
        /// </summary>
        /// <param name="uid">用户ID</param>
        /// <param name="roles">要设置得角色ID列表</param>
        /// <param name="accessToken">当前人员的访问Token</param>
        /// <returns></returns>
        public abstract Task<bool> SetUserRoles(long uid, List<long> roles, string accessToken);
        /// <summary>
        /// 获取用户的所有角色
        /// </summary>
        /// <param name="uid">用户ID</param>
        /// <returns></returns>
        public abstract Task<List<GCCRoles>> GetUserRoles(long uid);


        /// <summary>
        /// 登陆
        /// </summary>
        /// <param name="request">请求</param>
        /// <returns></returns>
        public abstract Task<string?> Login(GCCLoginRequest request);
        /// <summary>
        /// 登陆
        /// </summary>
        /// <param name="token">token</param>
        /// <returns></returns>
        public abstract Task Logout(string token);

        /// <summary>
        /// 设置用户的区域
        /// </summary>
        /// <param name="uid">用户ID</param>
        /// <param name="scopes">要设置得区域ID列表</param>
        /// <param name="accessToken">当前人员的访问Token</param>
        /// <returns></returns>
        public abstract Task<bool> SetUserScopes(long uid, List<long> scopes, string accessToken);
        /// <summary>
        /// 获取用户的所有角色
        /// </summary>
        /// <param name="uid">用户ID</param>
        /// <returns></returns>
        public abstract Task<List<GCCScopes>> GetUserScopes(long uid);


        /// <summary>
        /// 获取当前的认证的Token头
        /// </summary> 
        /// <returns>头</returns>
        public abstract string GetAuthHeaderKey();


        /// <summary>
        /// 給本地的MQ服务发送一条消息
        /// </summary>
        /// <param name="queueName">目标队列名</param> 
        /// <param name="message">消息内容</param>  
        /// <param name="virtualHost">虚拟主机名,默认/</param>
        /// <param name="exchangeName">特定的交换机名称,不填默认amq.direct,请IT人员配置好</param>
        /// <param name="routingKey">特定的交换机路径名,不填默认就是MQ的名字.请IT人员配置好</param>  
        /// <returns>是否推送成功</returns>
        public abstract bool SendMessageToLocalMQ(string message,
            string queueName,
            string virtualHost = "/",
            string? exchangeName = null,
            string? routingKey = null);

        /// <summary>
        /// 从本地的MQ中获取消息,连续性
        /// </summary>
        /// <param name="callBackMethod">回调函数,注意这是持续性的,考虑好释放时间,函数返回true则删除服务器消息,返回false则认为消费失败,重新回队列</param>       
        /// <param name="queueName">目标队列名</param> 
        /// <param name="virtualHost">虚拟主机名,默认/</param>
        /// <param name="canMutileThread">是否支持多线程,默认是true,如果填写false,那么将会进行串行回调</param>   
        /// <param name="exclusive">是否排他,也就是系统中不要出现第二个消费器</param>           
        /// <returns>channel的Token,用于关闭连接</returns>
        public abstract string? GotMessageFromLocalMQ(Func<string, bool> callBackMethod,
            string queueName,
            string virtualHost = "/",
            bool canMutileThread = true,
            bool exclusive = false);

        /// <summary>
        /// 从本地的MQ中获取消息,只触发一次
        /// </summary>        
        /// <param name="callBackMethod">回调函数,注意这是持续性的,考虑好释放时间,函数返回true则删除服务器消息,返回false则认为消费失败,重新回队列</param>      
        /// <param name="queueName">目标队列名</param> 
        /// <param name="virtualHost">虚拟主机名,默认/</param>       
        /// <param name="exclusive">是否排他,也就是系统中不要出现第二个消费器</param>    
        public abstract void GotOneMessageFromLocalMQ(Func<string, bool> callBackMethod,
            string queueName,
            string virtualHost = "/",
            bool exclusive = false);

        /// <summary>
        /// 給本地的MQ服务发送一条消息
        /// </summary>
        /// <param name="host">主机地址,只支持:amqp</param>
        /// <param name="userName">用户名</param>
        /// <param name="password">密码</param>
        /// <param name="queueName">目标队列名</param> 
        /// <param name="message">消息内容</param>  
        /// <param name="virtualHost">虚拟主机名,默认/</param>
        /// <param name="exchangeName">特定的交换机名称,不填默认amq.direct,请IT人员配置好</param>
        /// <param name="routingKey">特定的交换机路径名,不填默认就是MQ的名字.请IT人员配置好</param>  
        /// <returns>是否推送成功</returns>
        public abstract bool SendMessageToMQ(string host, string userName, string password, string message,
            string queueName,
            string virtualHost = "/",
            string? exchangeName = null,
            string? routingKey = null);

        /// <summary>
        /// 从本地的MQ中获取消息,连续性
        /// </summary>
        /// <param name="host">主机地址,只支持:amqp</param>
        /// <param name="userName">用户名</param>
        /// <param name="password">密码</param>
        /// <param name="callBackMethod">回调函数,注意这是持续性的,考虑好释放时间,函数返回true则删除服务器消息,返回false则认为消费失败,重新回队列</param>       
        /// <param name="queueName">目标队列名</param> 
        /// <param name="virtualHost">虚拟主机名,默认/</param>
        /// <param name="canMutileThread">是否支持多线程,默认是true,如果填写false,那么将会进行串行回调</param>   
        /// <param name="exclusive">是否排他,也就是系统中不要出现第二个消费器</param>   
        /// <returns>channel的Token,用于关闭连接</returns>
        public abstract string? GotMessageFromMQ(string host, string userName, string password, Func<string, bool> callBackMethod,
            string queueName,
            string virtualHost = "/",
            bool canMutileThread = true,
            bool exclusive = false);

        /// <summary>
        /// 从本地的MQ中获取消息,只触发一次
        /// </summary>        
        /// <param name="host">主机地址,只支持:amqp</param>
        /// <param name="userName">用户名</param>
        /// <param name="password">密码</param>
        /// <param name="callBackMethod">回调函数,注意这是持续性的,考虑好释放时间,函数返回true则删除服务器消息,返回false则认为消费失败,重新回队列</param>      
        /// <param name="queueName">目标队列名</param> 
        /// <param name="virtualHost">虚拟主机名,默认/</param>       
        /// <param name="exclusive">是否排他,也就是系统中不要出现第二个消费器</param>    
        public abstract void GotOneMessageFromMQ(string host, string userName, string password, Func<string, bool> callBackMethod,
            string queueName,
            string virtualHost = "/",
            bool exclusive = false);


        /// <summary>
        /// 关闭一个通道, 跟GotMessageFromLocalMQ配合使用
        /// </summary>        
        /// <param name="token">token</param>      
        public abstract void CloseMQConnection(string token);


        /// <summary>
        /// 发送一个跟踪信息
        /// </summary>
        /// <param name="type">类型</param> 
        /// <param name="resourceName">资源秒数</param> 
        /// <param name="value">信息</param>
        /// <param name="path">路径</param>
        /// <param name="from">从哪里来</param>
        /// <param name="method">函数</param> 
        public abstract void SaveTrace(string type,
            string resourceName = "",
            string path = "",
            string method = "",
            string from = "",
            string value = "");


        /// <summary>
        /// 通知一次连接事件,注意,这个跟你当前是API还是JOB有差别,如果你是APIContainer启动,那么这个就是WS服务,如果是JobContainer,那么就是客户端
        /// </summary>
        /// <param name="typeName"></param>
        public void WSConnectChanged(string typeName)
        {
            OnWSConnectChanged?.Invoke(typeName);
        }


        /// <summary>
        /// 获取所有的在线用户ID 注意,这个跟你当前是API还是JOB有差别,如果你是APIContainer启动,那么这个就是WS服务,如果是JobContainer,那么就是客户端
        /// </summary>
        /// <returns></returns>
        public abstract List<long> GetAllUserID();

        /// <summary>
        /// 获取用户的文本websocket客户端链接 注意,这个跟你当前是API还是JOB有差别,如果你是APIContainer启动,那么这个就是WS服务,如果是JobContainer,那么就是客户端
        /// </summary> 
        /// <param name="uid">用户ID</param>
        /// <returns></returns> 
        public abstract List<AbsWebsocketClient> GetUserWSClient<T>(long uid);

        /// <summary>
        /// 給某个用户发送文本的消息 注意,这个跟你当前是API还是JOB有差别,如果你是APIContainer启动,那么这个就是WS服务,如果是JobContainer,那么就是客户端
        /// </summary>
        /// <param name="uid">用户ID</param>
        /// <param name="text">文本</param>
        public abstract void SendTextMessageToUser(long uid, string text);
        /// <summary>
        /// 給某个用户的某个端发送文本的消息 注意,这个跟你当前是API还是JOB有差别,如果你是APIContainer启动,那么这个就是WS服务,如果是JobContainer,那么就是客户端
        /// </summary>
        /// <param name="uid">用户ID</param>
        /// <param name="clientID">用户clientID</param>
        /// <param name="text">文本</param>
        public abstract void SendTextMessageToUserClient(long uid, string clientID, string text);

        /// <summary>
        /// 给所有在线的人广播 注意,这个跟你当前是API还是JOB有差别,如果你是APIContainer启动,那么这个就是WS服务,如果是JobContainer,那么就是客户端
        /// </summary>
        /// <param name="text"></param>
        public abstract void BoardCastTextMessage(string text);

        /// <summary>
        /// 强制某个类型的端下线 注意,这个跟你当前是API还是JOB有差别,如果你是APIContainer启动,那么这个就是WS服务,如果是JobContainer,那么就是客户端
        /// </summary>
        /// <param name="uid">用户ID</param>
        /// <param name="clientID">端ID不传就是全部</param>
        public abstract void ForcedOffline<T>(long uid, string? clientID);



        /// <summary>
        /// 转换坐标系
        /// </summary>
        /// <param name="from">从哪个 比如  EPSG:3857</param>
        /// <param name="to">目标转换为 比如 EPSG:4326</param>
        /// <param name="sourceX">原始坐标X</param>
        /// <param name="sourceY">原始坐标Y</param>
        /// <param name="targetX">转换后X</param>
        /// <param name="targetY">转换后Y</param>
        public abstract void TransformCoordinate(string from, string to, double sourceX, double sourceY, out double targetX, out double targetY);


        /// <summary>
        /// 通过自定义算法来转坐标
        /// </summary>
        /// <param name="fromName">算法名字</param>
        /// <param name="fromContent">原始坐标系算法</param>
        /// <param name="toName">目标坐标系名字</param>
        /// <param name="toContext">目标坐标系算法</param>
        /// <param name="sourceX">原始坐标X</param>
        /// <param name="sourceY">原始坐标Y</param>
        /// <param name="targetX">转换后X</param>
        /// <param name="targetY">转换后Y</param>
        public abstract void TransformCoordinate(string fromName, string fromContent, string toName, string toContext, double sourceX, double sourceY, out double targetX, out double targetY);

    }
}
